use rusqlite::{params, Connection, Result as SqliteResult};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use thiserror::Error;
use tokio::sync::Mutex;

#[derive(Debug, Error)]
pub enum CallHistoryError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] rusqlite::Error),
    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),
    #[error("Time error: {0}")]
    TimeError(String),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum CallDirection {
    Inbound,
    Outbound,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum CallEndReason {
    Normal,
    Busy,
    NoAnswer,
    Failed,
    Cancelled,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallRecord {
    pub id: String,
    pub remote_number: String,
    pub remote_name: Option<String>,
    pub direction: CallDirection,
    pub start_time: SystemTime,
    pub duration: Option<Duration>,
    pub status: CallEndReason,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallHistoryFilter {
    pub direction: Option<CallDirection>,
    pub start_date: Option<SystemTime>,
    pub end_date: Option<SystemTime>,
    pub limit: Option<u32>,
    pub offset: Option<u32>,
}

pub struct CallHistoryManager {
    db_path: PathBuf,
    _init_lock: Arc<Mutex<()>>, // Used to ensure database is initialized only once
}

impl CallHistoryManager {
    pub fn new(db_path: PathBuf) -> Result<Self, CallHistoryError> {
        let manager = Self {
            db_path: db_path.clone(),
            _init_lock: Arc::new(Mutex::new(())),
        };
        
        // Initialize database synchronously
        manager.init_database_sync()?;
        
        Ok(manager)
    }

    fn init_database_sync(&self) -> Result<(), CallHistoryError> {
        let conn = Connection::open(&self.db_path)?;
        
        // Create call_history table
        conn.execute(
            "CREATE TABLE IF NOT EXISTS call_history (
                id TEXT PRIMARY KEY,
                remote_number TEXT NOT NULL,
                remote_name TEXT,
                direction TEXT NOT NULL,
                start_time INTEGER NOT NULL,
                duration INTEGER,
                status TEXT NOT NULL,
                created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
            )",
            [],
        )?;

        // Create index for faster queries
        conn.execute(
            "CREATE INDEX IF NOT EXISTS idx_call_history_start_time ON call_history(start_time)",
            [],
        )?;

        conn.execute(
            "CREATE INDEX IF NOT EXISTS idx_call_history_direction ON call_history(direction)",
            [],
        )?;

        log::info!("Call history database initialized at: {:?}", self.db_path);
        
        Ok(())
    }

    fn get_connection(&self) -> Result<Connection, CallHistoryError> {
        Ok(Connection::open(&self.db_path)?)
    }

    pub async fn add_call_record(&self, record: CallRecord) -> Result<(), CallHistoryError> {
        let conn = self.get_connection()?;
        
        let start_time_secs = record.start_time
            .duration_since(UNIX_EPOCH)
            .map_err(|e| CallHistoryError::TimeError(e.to_string()))?
            .as_secs() as i64;

        let duration_secs = record.duration.map(|d| d.as_secs() as i64);
        let direction_str = match record.direction {
            CallDirection::Inbound => "inbound",
            CallDirection::Outbound => "outbound",
        };
        let status_str = match record.status {
            CallEndReason::Normal => "normal",
            CallEndReason::Busy => "busy",
            CallEndReason::NoAnswer => "no_answer",
            CallEndReason::Failed => "failed",
            CallEndReason::Cancelled => "cancelled",
        };

        conn.execute(
            "INSERT INTO call_history (id, remote_number, remote_name, direction, start_time, duration, status)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![
                record.id,
                record.remote_number,
                record.remote_name,
                direction_str,
                start_time_secs,
                duration_secs,
                status_str
            ],
        )?;

        log::info!("Added call record: {} -> {}", record.remote_number, record.id);
        
        Ok(())
    } 
    pub async fn get_call_history(&self, filter: Option<CallHistoryFilter>) -> Result<Vec<CallRecord>, CallHistoryError> {
        let conn = self.get_connection()?;
        
        let mut query = "SELECT id, remote_number, remote_name, direction, start_time, duration, status FROM call_history".to_string();
        let mut conditions = Vec::new();
        let mut params = Vec::new();

        if let Some(filter) = filter {
            if let Some(direction) = filter.direction {
                conditions.push("direction = ?".to_string());
                params.push(match direction {
                    CallDirection::Inbound => "inbound".to_string(),
                    CallDirection::Outbound => "outbound".to_string(),
                });
            }

            if let Some(start_date) = filter.start_date {
                conditions.push("start_time >= ?".to_string());
                let start_secs = start_date
                    .duration_since(UNIX_EPOCH)
                    .map_err(|e| CallHistoryError::TimeError(e.to_string()))?
                    .as_secs() as i64;
                params.push(start_secs.to_string());
            }

            if let Some(end_date) = filter.end_date {
                conditions.push("start_time <= ?".to_string());
                let end_secs = end_date
                    .duration_since(UNIX_EPOCH)
                    .map_err(|e| CallHistoryError::TimeError(e.to_string()))?
                    .as_secs() as i64;
                params.push(end_secs.to_string());
            }

            if !conditions.is_empty() {
                query.push_str(" WHERE ");
                query.push_str(&conditions.join(" AND "));
            }

            query.push_str(" ORDER BY start_time DESC");

            if let Some(limit) = filter.limit {
                query.push_str(&format!(" LIMIT {}", limit));
                if let Some(offset) = filter.offset {
                    query.push_str(&format!(" OFFSET {}", offset));
                }
            }
        } else {
            query.push_str(" ORDER BY start_time DESC");
        }

        let mut stmt = conn.prepare(&query)?;
        let param_refs: Vec<&dyn rusqlite::ToSql> = params.iter().map(|p| p as &dyn rusqlite::ToSql).collect();
        
        let rows = stmt.query_map(&param_refs[..], |row| {
            let direction_str: String = row.get(3)?;
            let direction = match direction_str.as_str() {
                "inbound" => CallDirection::Inbound,
                "outbound" => CallDirection::Outbound,
                _ => CallDirection::Outbound, // Default fallback
            };

            let status_str: String = row.get(6)?;
            let status = match status_str.as_str() {
                "normal" => CallEndReason::Normal,
                "busy" => CallEndReason::Busy,
                "no_answer" => CallEndReason::NoAnswer,
                "failed" => CallEndReason::Failed,
                "cancelled" => CallEndReason::Cancelled,
                _ => CallEndReason::Failed, // Default fallback
            };

            let start_time_secs: i64 = row.get(4)?;
            let start_time = UNIX_EPOCH + Duration::from_secs(start_time_secs as u64);

            let duration = if let Ok(duration_secs) = row.get::<_, Option<i64>>(5) {
                duration_secs.map(|secs| Duration::from_secs(secs as u64))
            } else {
                None
            };

            Ok(CallRecord {
                id: row.get(0)?,
                remote_number: row.get(1)?,
                remote_name: row.get(2)?,
                direction,
                start_time,
                duration,
                status,
            })
        })?;

        let mut records = Vec::new();
        for row in rows {
            records.push(row?);
        }

        Ok(records)
    }

    pub async fn clear_call_history(&self) -> Result<(), CallHistoryError> {
        let conn = self.get_connection()?;
        conn.execute("DELETE FROM call_history", [])?;
        log::info!("Call history cleared");
        Ok(())
    }

    pub async fn get_call_count(&self) -> Result<u32, CallHistoryError> {
        let conn = self.get_connection()?;
        let count: i64 = conn.query_row("SELECT COUNT(*) FROM call_history", [], |row| row.get(0))?;
        Ok(count as u32)
    }

    pub async fn delete_call_record(&self, call_id: &str) -> Result<bool, CallHistoryError> {
        let conn = self.get_connection()?;
        let rows_affected = conn.execute("DELETE FROM call_history WHERE id = ?1", params![call_id])?;
        Ok(rows_affected > 0)
    }

    pub async fn get_recent_numbers(&self, limit: u32) -> Result<Vec<String>, CallHistoryError> {
        let conn = self.get_connection()?;
        let mut stmt = conn.prepare(
            "SELECT DISTINCT remote_number FROM call_history 
             ORDER BY start_time DESC LIMIT ?"
        )?;
        
        let rows = stmt.query_map(params![limit], |row| {
            Ok(row.get::<_, String>(0)?)
        })?;

        let mut numbers = Vec::new();
        for row in rows {
            numbers.push(row?);
        }

        Ok(numbers)
    }
}

impl Clone for CallHistoryManager {
    fn clone(&self) -> Self {
        Self {
            db_path: self.db_path.clone(),
            _init_lock: self._init_lock.clone(),
        }
    }
}

// Helper function to convert CallInfo to CallRecord
impl From<crate::call_manager::CallInfo> for CallRecord {
    fn from(call_info: crate::call_manager::CallInfo) -> Self {
        let status = match call_info.status {
            crate::call_manager::CallStatus::Ended => CallEndReason::Normal,
            crate::call_manager::CallStatus::Failed => CallEndReason::Failed,
            crate::call_manager::CallStatus::Ringing => CallEndReason::Cancelled,
            crate::call_manager::CallStatus::Connected => CallEndReason::Normal,
        };

        let direction = match call_info.direction {
            crate::call_manager::CallDirection::Inbound => CallDirection::Inbound,
            crate::call_manager::CallDirection::Outbound => CallDirection::Outbound,
        };

        CallRecord {
            id: call_info.id,
            remote_number: call_info.remote_number,
            remote_name: call_info.remote_name,
            direction,
            start_time: call_info.start_time.unwrap_or_else(SystemTime::now),
            duration: call_info.duration,
            status,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::{Duration, SystemTime};
    use tempfile::tempdir;

    #[tokio::test]
    async fn test_call_history_manager_creation() {
        let temp_dir = tempdir().unwrap();
        let db_path = temp_dir.path().join("test_call_history.db");
        
        let manager = CallHistoryManager::new(db_path).unwrap();
        
        // Test that we can get an empty call history
        let history = manager.get_call_history(None).await.unwrap();
        assert!(history.is_empty());
    }

    #[tokio::test]
    async fn test_add_and_retrieve_call_record() {
        let temp_dir = tempdir().unwrap();
        let db_path = temp_dir.path().join("test_call_history.db");
        
        let manager = CallHistoryManager::new(db_path).unwrap();
        
        let record = CallRecord {
            id: "test_call_1".to_string(),
            remote_number: "1234567890".to_string(),
            remote_name: Some("Test User".to_string()),
            direction: CallDirection::Outbound,
            start_time: SystemTime::now(),
            duration: Some(Duration::from_secs(120)),
            status: CallEndReason::Normal,
        };
        
        // Add the record
        manager.add_call_record(record.clone()).await.unwrap();
        
        // Retrieve and verify
        let history = manager.get_call_history(None).await.unwrap();
        assert_eq!(history.len(), 1);
        assert_eq!(history[0].id, record.id);
        assert_eq!(history[0].remote_number, record.remote_number);
        assert_eq!(history[0].direction, record.direction);
        assert_eq!(history[0].status, record.status);
    }

    #[tokio::test]
    async fn test_call_count() {
        let temp_dir = tempdir().unwrap();
        let db_path = temp_dir.path().join("test_call_history.db");
        
        let manager = CallHistoryManager::new(db_path).unwrap();
        
        // Initially should be 0
        let count = manager.get_call_count().await.unwrap();
        assert_eq!(count, 0);
        
        // Add a record
        let record = CallRecord {
            id: "test_call_1".to_string(),
            remote_number: "1234567890".to_string(),
            remote_name: None,
            direction: CallDirection::Inbound,
            start_time: SystemTime::now(),
            duration: None,
            status: CallEndReason::Cancelled,
        };
        
        manager.add_call_record(record).await.unwrap();
        
        // Should now be 1
        let count = manager.get_call_count().await.unwrap();
        assert_eq!(count, 1);
    }

    #[tokio::test]
    async fn test_clear_call_history() {
        let temp_dir = tempdir().unwrap();
        let db_path = temp_dir.path().join("test_call_history.db");
        
        let manager = CallHistoryManager::new(db_path).unwrap();
        
        // Add a record
        let record = CallRecord {
            id: "test_call_1".to_string(),
            remote_number: "1234567890".to_string(),
            remote_name: None,
            direction: CallDirection::Outbound,
            start_time: SystemTime::now(),
            duration: Some(Duration::from_secs(60)),
            status: CallEndReason::Normal,
        };
        
        manager.add_call_record(record).await.unwrap();
        
        // Verify it's there
        let count = manager.get_call_count().await.unwrap();
        assert_eq!(count, 1);
        
        // Clear history
        manager.clear_call_history().await.unwrap();
        
        // Verify it's gone
        let count = manager.get_call_count().await.unwrap();
        assert_eq!(count, 0);
    }

    #[tokio::test]
    async fn test_get_recent_numbers() {
        let temp_dir = tempdir().unwrap();
        let db_path = temp_dir.path().join("test_call_history.db");
        
        let manager = CallHistoryManager::new(db_path).unwrap();
        
        // Add multiple records with different numbers
        let numbers = vec!["1111111111", "2222222222", "3333333333"];
        
        for (i, number) in numbers.iter().enumerate() {
            let record = CallRecord {
                id: format!("test_call_{}", i),
                remote_number: number.to_string(),
                remote_name: None,
                direction: CallDirection::Outbound,
                start_time: SystemTime::now(),
                duration: Some(Duration::from_secs(30)),
                status: CallEndReason::Normal,
            };
            
            manager.add_call_record(record).await.unwrap();
        }
        
        // Get recent numbers
        let recent = manager.get_recent_numbers(5).await.unwrap();
        assert_eq!(recent.len(), 3);
        
        // Should contain all our numbers
        for number in &numbers {
            assert!(recent.contains(&number.to_string()));
        }
    }
}